/*
 * Copyright (c) 2008-2018, RF-Embedded GmbH
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 *  1. Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, 
 *     this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using CSrfeReaderInterface.device;
using CSrfeReaderInterface.protocol;
using CSrfeReaderInterface.impl.device;
using CSrfeReaderInterface;

namespace CSrfeTest_ConsoleApplication
{
    class TestProgram
    {
        public enum ConnectionType
        {
            SERIAL,
            TCP
        }

        public enum ReaderType
        {
            STANDARD
        }

        protected ConnectionType _connectionType;
        protected string _serialPortName;
        protected string _ipAddress;
        protected ushort _tcpPort;

        protected ReaderType _readerType;

        protected CSrfeProtocolHandler _ph = null;
        protected bool _init = false;
        protected int _testTraceLevel = 0;

        protected CSrfeProtocolHandler.HeartBeatHandler _onHeratBeat = null;
        protected CSrfeProtocolHandler.StateChangedHandler _onStateChanged = null;
        protected CSrfeProtocolHandler.GpioValuesChangedHandler _onGpioValuesChanged = null;
        protected CSrfeProtocolHandler.CyclicInventoryHandler _onCyclicInventory = null;
        protected CSrfeProtocolHandler.ErrorEventHandler _onErrorOccured = null;



        public TestProgram()
        {
            _ph = null;
            _init = false;
        }

        public bool init(IProtocolDeviceInterface device, ReaderType type)
        {
            // create trace and turn off 
            Global.m_tracer = new Trace.ConsoleTrace();
            Global.m_tracer.TraceLevel = _testTraceLevel;

            _readerType = type;

            SerialDevice serial = device as SerialDevice;
            if (serial != null)
            {
                _serialPortName = serial.PortName();
                _connectionType = ConnectionType.SERIAL;
            }

            TCPDevice tcp = device as TCPDevice;
            if (tcp != null)
            {
                _ipAddress = tcp.IpAddress();
                _tcpPort = (ushort)tcp.Port();
                _connectionType = ConnectionType.TCP;
            }

            _ph = createInstance(device, type);

            if (_ph == null)
                return false;

            _ph.setHeartBeat(false, 0);

            // connect events to the local event handler
            _ph.HeartBeat += _onHeratBeat = new CSrfeProtocolHandler.HeartBeatHandler(onHeratBeat);
            _ph.StateChanged += _onStateChanged = new CSrfeProtocolHandler.StateChangedHandler(onStateChanged);
            _ph.GpioValuesChanged += _onGpioValuesChanged = new CSrfeProtocolHandler.GpioValuesChangedHandler(onGpioValuesChanged);
            _ph.CyclicInventory += _onCyclicInventory = new CSrfeProtocolHandler.CyclicInventoryHandler(onCyclicInventory);
            _ph.ErrorEvent += _onErrorOccured = new CSrfeProtocolHandler.ErrorEventHandler(onErrorOccured);

            _init = true;

            return true;
        }

        public bool exec()
        {
            if (!_init)
                return false;

            Console.Clear();
            Console.WriteLine("");
            printTestTable();

            Console.Write("Please choose: > ");
            string idStr = Console.ReadLine();

            Console.WriteLine("");

            if (idStr == "q")
            {
                Console.WriteLine("Exit");
                return false;
            }

            int id = 0;

            try
            {
                id = Convert.ToInt32(idStr);
            }
            catch (Exception /*e*/)
            {
                Console.WriteLine("Input string is not a sequence of digits.");
                return true;
            }

            if (callTestRoutine(id) == false)
            {
                Console.WriteLine("Not yet implemented");
            }


            Console.WriteLine("\n\n\n");
            Console.WriteLine("Press enter to proceed...");
            Console.ReadLine();

            return true;
        }


        #region Test Methods

            #region Common

                public void test_ReaderInfo()
                {
                    bool ok = false;

                    // get reader id
                    uint readerId = 0;
                    ok = _ph.getReaderID(out readerId);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get ReaderID");

                    // get reader type
                    uint readerType = 0;
                    ok = _ph.getReaderType(out readerType);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get ReaderType");

                    // get hardware revision
                    uint hwRev = 0;
                    ok = _ph.getHardwareRevision(out hwRev);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get HardwareRevision");

                    // get software revision
                    uint swRev = 0;
                    ok = _ph.getSoftwareRevision(out swRev);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get SoftwareRevision");

                    // get bootloader revision
                    uint blRev = 0;
                    ok = _ph.getBootloaderRevision(out blRev);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get BootloaderRevision");

                    // get current system
                    Constants.eRFE_CURRENT_SYSTEM system = 0;
                    ok = _ph.getCurrentSystem(out system);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get CurrentSystem");

                    // get current state
                    Constants.eRFE_CURRENT_READER_STATE state = 0;
                    ok = _ph.getCurrentState(out state);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get CurrentState");

                    // get status register
                    ulong statusReg = 0;
                    ok = _ph.getStatusRegister(out statusReg);
                    if (!ok)
                        Console.WriteLine("ERROR: Could not get StatusRegister");

                    // print out results
                    Console.WriteLine("Reader Information:");
                    Console.WriteLine("\t -> ReaderID       = 0x{0:X08}", readerId);
                    Console.WriteLine("\t -> ReaderType     = 0x{0:X08}", readerType);
                    Console.WriteLine("\t -> HardwareRev    = 0x{0:X08}", hwRev);
                    Console.WriteLine("\t -> SoftwareRev    = 0x{0:X08}", swRev);
                    Console.WriteLine("\t -> BootloaderRev  = 0x{0:X08}", blRev);
                    Console.WriteLine("\t -> Current System = {0}", system.ToString());
                    Console.WriteLine("\t -> Current State  = {0}", state.ToString());
                    Console.WriteLine("\t -> StatusRegister = 0x{0:X016}", statusReg);
                }

                public void test_Attenuation()
                {
                    Console.WriteLine("Testing attenuation settings:");

                    ushort attMax, attCur, attTemp;

                    // get attenuation values
                    Console.WriteLine("\t -> 1, Reading attenuation settings:");
                    _ph.getAttenuation(out attMax, out attCur);
                    Console.WriteLine("\t\t Attenuation Max={0} Current={1}", attMax, attCur);
                    Console.WriteLine("");

                    // set attenuation to fix value 10
                    Console.WriteLine("\t -> 2, Setting attenuation settings:");
                    _ph.setAttenuation(10);
                    Console.WriteLine("\t\t Set Attenuation to 10");
                    Console.WriteLine("");

                    // get attenuation settings and check if the fix value is set
                    Console.WriteLine("\t -> 3, Reading attenuation settings:");
                    _ph.getAttenuation(out attMax, out attTemp);
                    Console.WriteLine("\t\t Attenuation Max={0} Current={1}", attMax, attTemp);
                    Console.WriteLine("\t\t Current attenuation {0} == 10?", attTemp);
                    if (attTemp != 10)
                    {
                        Console.WriteLine("ERROR------------------ Set Attenuation is not the Current Attenuation");
                        return;
                    }
                    Console.WriteLine("\t\t OK\n");

                    // retore attenuation to the previous value
                    Console.WriteLine("\t -> 4, Restore attenuation settings:");
                    _ph.setAttenuation(attCur);
                    Console.WriteLine("\t\t Set Attenuation to {0}", attCur);
                    Console.WriteLine("");

                    // check the set values again
                    Console.WriteLine("\t -> 5, Reading attenuation settings:");
                    _ph.getAttenuation(out attMax, out attCur);
                    Console.WriteLine("\t\t Attenuation Max={0} Current={1}", attMax, attCur);
                    Console.WriteLine("");
                }

                public void test_Frequency()
                {
                    Console.WriteLine("Testing frequency settings:");

                    byte mode, maxCount;
                    List<uint> frequ;
                    List<uint> tempFrequ;

                    // get current frequency table
                    Console.WriteLine("\t -> 1, Reading frequency settings:");
                    _ph.getFrequency(out mode, out maxCount, out frequ);
                    Console.WriteLine("\t\t FrequencyTable Mode={0} Max={1} Current={2}", mode, maxCount, frequ.Count());
                    for (int i = 0; i < frequ.Count(); i++)
                    {
                        Console.WriteLine("\t\t\t {0} = {1}", i, frequ[i]);
                    }
                    Console.WriteLine("");
                }

                public void test_Sensitivity()
                {
                    Console.WriteLine("Testing sensitivity settings:");

                    short maxSens, minSens, curSens, temSens, actSens;

                    // get current sensitivity
                    Console.WriteLine("\t -> 1, Reading sensitivity settings:");
                    _ph.getSensitivity(out maxSens, out minSens, out curSens);
                    Console.WriteLine("\t\t Sensitivity Max={0} Min={1} Current={2}", maxSens, minSens, curSens);
                    Console.WriteLine("");
                }

                public void test_Heartbeat()
                {
                    Console.WriteLine("Testing Heartbeat:");

                    // turn on heartbeat with an interval of 250ms
                    Console.WriteLine("\t -> 1, Setting Heartbeat ON with interval 250ms (Press Enter to Stop)");
                    _lastHeartbeat = DateTime.Now;
                    _ph.setHeartBeat(true, 250);

                    // wait for <enter>
                    Console.ReadLine();

                    // turn off heartbeat
                    Console.WriteLine("\t -> 2, Setting Heartbeat OFF");
                    _ph.setHeartBeat(false, 250);
                }

                public void test_GPIO()
                {
                    Console.WriteLine("Test GPIO");

                    ulong mask, output, input, level;
                    if (!_ph.getGPIOCaps(out mask, out output, out input))
                    {
                        Console.WriteLine("ERROR: Could not get GPIO Caps: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("Available pins: 0x{0:X08}", mask);
                    Console.WriteLine("Output pins: 0x{0:X08}", output);
                    Console.WriteLine("Input pins: 0x{0:X08}", input);

                    if (!_ph.getGPIO(out level))
                    {
                        Console.WriteLine("ERROR: Could not get GPIO: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("Pin levels: 0x{0:X08}", level);
                }

                public void test_AntennaSetting()
                {
                    Console.WriteLine("Trying Antenna Settings:");

                    // Check how much antennas the reader supports
                    Console.WriteLine("\t -> 1, Get antenna count:");
                    byte antennaCount = 0;
                    _ph.getAntennaCount(out antennaCount);
                    Console.WriteLine("\t\tThe reader has " + antennaCount + " antenna connectors.");
                    Console.WriteLine("");

                    if (antennaCount <= 1)
                    {
                        Console.WriteLine("\tThe reader only offers 1 antenna, so no other can be selected.");
                        return;
                    }
                }

                public void test_Inventory()
                {
                    Console.WriteLine("Testing Cyclic Inventory:");

                    // turn on cyclic inventory
                    Console.WriteLine("\t -> 1, Starting Cyclic Inventory (Press Enter to Stop)");
                    _tagReadCount = 0;
                    DateTime startTime = DateTime.Now;
                    _ph.setCyclicInventory(true);

                    // wait for <enter>
                    Console.ReadLine();

                    // turn off cyclic inventory and calculate read rate
                    _ph.setCyclicInventory(false);
                    double secs = DateTime.Now.Subtract(startTime).TotalSeconds;
                    Console.WriteLine("\t -> 2, Stopped Cyclic Inventry with a ReadRate of {0} reads/sec", _tagReadCount / secs);
                }

                public void test_SingleInventory()
                {
                    // do a single inventory and print out the list
                    Console.WriteLine("Do SingleInventory");
                    List<CSrfeProtocolHandler.TagEvent> tagList;

                    DateTime startTime = DateTime.Now;
                    _ph.doSingleInventory(out tagList);
                    DateTime stopTime = DateTime.Now;


                    Console.WriteLine("\t\t -> Found {0} Tags after {1}", tagList.Count, stopTime.Subtract(startTime).TotalMilliseconds);
                    int i = 1;
                    foreach (CSrfeProtocolHandler.TagEvent t in tagList)
                        Console.WriteLine("\t\t #{0} {1}", i++, t.ToString());
                }

                public void test_ReadTIDUM()
                {
                    if (!check_antennaSelection())
                        return;

                    Console.WriteLine("Trying to read tid register:");

                    // do a single inventory and print out the tag list with an index to select one tag
                    Console.WriteLine("\t -> 1, Searching for tags:");
                    List<CSrfeProtocolHandler.TagEvent> tagList;
                    _ph.doSingleInventory(out tagList);
                    Console.WriteLine("\t\t Found {0} Tags", tagList.Count);
                    if (tagList.Count() > 0)
                    {
                        for (int i = 0; i < tagList.Count(); i++)
                            Console.WriteLine("\t\t\t [{0}]: {1}", i + 1, BitConverter.ToString(tagList[i].tagId));
                    }
                    else
                    {
                        Console.WriteLine("\t\t Found 0 Tags, stopping the test...");
                        return;
                    }
                    Console.WriteLine("");

                    // read the selected index
                    Console.Write("\t -> 2, Select a tag by index: > ");
                    int k = Console.ReadLine()[0];
                    int index = k - '0' - 1;
                    if (index < 0 || index >= tagList.Count())
                    {
                        Console.WriteLine("\t\t Selected unkown tag, stopping the test...");
                        return;
                    }
                    Console.WriteLine("");

                    ////////////////////////////////////////////////// READ
                    // try to read the first 4 bytes of the TID membank of the selected tag
                    Console.WriteLine("\t -> 3, Trying to read TID register:");
                    byte[] passwd = new byte[4] { 0, 0, 0, 0 };
                    byte[] data;
                    if (_ph.readFromTag(tagList[index].tagId, (byte)0x02, 0, passwd, 4, out data))
                    {
                        Console.WriteLine("\t\t Read succeeded, read data:");
                        Console.Write("\t\t\t ");
                        for (int i = 0; i < data.Length; i++)
                        {
                            Console.Write("{0:X02}-", data[i]);
                            if (((i + 1) % 8) == 0)
                                Console.Write("\n\t\t\t ");
                        }
                        Console.Write("\n");
                    }
                    else
                    {
                        Console.WriteLine("\t\t Read failed");
                    }
                    Console.WriteLine("");

                    ////////////////////////////////////////////////// READ
                    // try to read the first 4 bytes of the user memory bank
                    Console.WriteLine("\t -> 4, Trying to read UM register:");
                    if (_ph.readFromTag(tagList[index].tagId, 0x03, 0, passwd, 4, out data))
                    {
                        Console.WriteLine("\t\t Read succeeded, read data:");
                        Console.Write("\t\t\t ");
                        for (int i = 0; i < data.Length; i++)
                        {
                            Console.Write("{0:X02}-", data[i]);
                            if (((i + 1) % 8) == 0)
                                Console.Write("\n\t\t\t ");
                        }
                        Console.Write("\n");
                    }
                    else
                    {
                        Console.WriteLine("\t\t Read failed: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("");
                }

                public void test_ReadWriteUser()
                {
                    if (!check_antennaSelection())
                        return;

                    Console.WriteLine("Trying to read and write user register:");

                    // do a single inventory and print out the tag list with an index to select one tag
                    Console.WriteLine("\t -> 1, Searching for tags:");
                    List<CSrfeProtocolHandler.TagEvent> tagList;
                    _ph.doSingleInventory(out tagList);
                    Console.WriteLine("\t\t Found {0} Tags", tagList.Count());
                    if (tagList.Count() > 0)
                    {
                        for (int i = 0; i < tagList.Count(); i++)
                            Console.WriteLine("\t\t\t [{0}]: {1}", i + 1, BitConverter.ToString(tagList[i].tagId));
                    }
                    else
                    {
                        Console.WriteLine("\t\t Found 0 Tags, stopping the test...");
                        return;
                    }
                    Console.WriteLine("");

                    // read the selected index
                    Console.Write("\t -> 2, Select a tag by index: > ");
                    int k = Console.ReadLine()[0];
                    int index = k - '0' - 1;
                    if (index < 0 || index >= tagList.Count())
                    {
                        Console.WriteLine("\t\t Selected unkown tag, stopping the test...");
                        return;
                    }
                    Console.WriteLine("");

                    byte memBank = 0x03;
                    byte[] passwd = new byte[4] { 0, 0, 0, 0 };
                    byte[] userMemBefore;
                    byte[] userMemAfter;
                    byte[] old4Bytes = new byte[4];
                    byte[] new4Bytes = new byte[4] { 0x11, 0x22, 0x33, 0x44 };

                    // try to read the whole user memory bank
                    Console.WriteLine("\t -> 3, Trying to Read 6 Bytes from user mem");
                    if (_ph.readFromTag(tagList[index].tagId, memBank, 0, passwd, 6, out userMemBefore))
                    {
                        Console.WriteLine("\t\t Read succeeded, read data:");
                        Console.Write("\t\t\t ");
                        for (int i = 0; i < userMemBefore.Length; i++)
                        {
                            Console.Write("{0:X02}-", userMemBefore[i]);
                            if (((i + 1) % 8) == 0)
                                Console.Write("\n\t\t\t ");
                        }
                        Console.Write("\n");
                    }
                    else
                    {
                        Console.WriteLine("\t\t Read failed: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("");

                    // save the old first 4 bytes of the user memory
                    old4Bytes[0] = userMemBefore[0];
                    old4Bytes[1] = userMemBefore[1];
                    old4Bytes[2] = userMemBefore[2];
                    old4Bytes[3] = userMemBefore[3];

                    // try to write 4 dummy bytes to the user mem
                    Console.WriteLine("\t -> 4, Trying to Write data:");
                    Console.WriteLine("\t\t\t {0}", BitConverter.ToString(new4Bytes));
                    if (_ph.writeToTag(tagList[index].tagId, memBank, 0, passwd, new4Bytes))
                    {
                        Console.WriteLine("\t\t Write succeeded");
                    }
                    else
                    {
                        Console.WriteLine("\t\t Write failed: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("");

                    // try to read the whole user me again
                    Console.WriteLine("\t -> 5, Trying to Read written data");
                    if (_ph.readFromTag(tagList[index].tagId, memBank, 0, passwd, 6, out userMemAfter))
                    {
                        Console.WriteLine("\t\t Read succeeded, read data:");
                        Console.Write("\t\t\t ");
                        for (int i = 0; i < userMemAfter.Length; i++)
                        {
                            Console.Write("{0:X02}-", userMemAfter[i]);
                            if (((i + 1) % 8) == 0)
                                Console.Write("\n\t\t\t ");
                        }
                        Console.Write("\n");
                    }
                    else
                    {
                        Console.WriteLine("\t\t Read failed: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("");

                    // try to write the old bytes into the memory
                    Console.WriteLine("\t -> 6, Trying to restore the bytes");
                    if (_ph.writeToTag(tagList[index].tagId, memBank, 0, passwd, userMemBefore))
                    {
                        Console.WriteLine("\t\t Wrtie succeeded");
                    }
                    else
                    {
                        Console.WriteLine("\t\t Wrtie failed: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("");

                    // again read the restored bytes from the tag
                    Console.WriteLine("\t -> 7, Trying to Read written data");
                    if (_ph.readFromTag(tagList[index].tagId, memBank, 0, passwd, 6, out userMemAfter))
                    {
                        Console.WriteLine("\t\t Read succeeded, read data: ");
                        Console.Write("\t\t\t {0}", BitConverter.ToString(userMemAfter));
                    }
                    else
                    {
                        Console.WriteLine("\t\t Read failed: " + _ph.LastReturnCode.ToString());
                        return;
                    }
                    Console.WriteLine("");
                }

            #endregion

                public void ReadTIDFirstTag_Slow()
                {
                    if (!check_antennaSelection())
                        return;

                    Console.WriteLine("Trying to read tid register:");

                    _storedTagIdSema = new Semaphore(0, 1000);
                    _storedTagEvent.Clear();
                    CSrfeProtocolHandler.CyclicInventoryHandler newEventHandler = new CSrfeProtocolHandler.CyclicInventoryHandler(onCyclicInventoryStoreTagId);
                    _ph.CyclicInventory += newEventHandler;

                    Console.WriteLine("\t -> 1, Searching for tags:");

                    _tagReadCount = 0;
                    _ph.setCyclicInventory(true);

                    _storedTagIdSema.WaitOne(5000);

                    _ph.setCyclicInventory(false);

                    if (_storedTagEvent.Count == 0)
                    {
                        Console.WriteLine("\t\t Did not find any tag...");
                        _ph.CyclicInventory -= newEventHandler;
                        return;
                    }

                    Console.WriteLine("\t\t Found Tag " + _storedTagEvent.FirstOrDefault().ToString());

                    // Set working antenna for read operation
                    if (_storedTagEvent.FirstOrDefault().hasAntenna)
                        _ph.setWorkingAntenna(_storedTagEvent.FirstOrDefault().antennaId);

                    // try to read the first 4 bytes of the TID membank of the detected tag
                    Console.WriteLine("\t -> 2, Trying to read first 4 bytes of TID register:");
                    byte[] passwd = new byte[4] { 0, 0, 0, 0 };
                    byte[] data;

                    DateTime startTime = DateTime.Now;
                    bool result = _ph.readFromTag(_storedTagEvent.FirstOrDefault().tagId, (byte)0x02, 0, passwd, 2, out data);
                    DateTime stopTime = DateTime.Now;
                    
                    if (result)
                    {
                        Console.WriteLine("\t\t Read succeeded, read data: {0}", BitConverter.ToString(data));
                    }
                    else
                    {
                        Console.WriteLine("\t\t Read failed");
                    }

                    Console.WriteLine("");
                    Console.WriteLine("Read Result {0}ms after the tag was detected", stopTime.Subtract(startTime).TotalMilliseconds);
                    Console.WriteLine("");

                    _ph.CyclicInventory -= newEventHandler;
                }

        #endregion Test Methods





        #region Event Handler

        protected DateTime _lastHeartbeat = DateTime.Now;

            // event handler for the heartbeat event
            public void onHeratBeat(byte[] data)
            {
                Console.WriteLine("\t\t Heartbeat after {0} ms / data: {1}", DateTime.Now.Subtract(_lastHeartbeat).TotalMilliseconds, BitConverter.ToString(data));
                _lastHeartbeat = DateTime.Now;
            }

            // event handler for the state changed event
            public void onStateChanged(Constants.eRFE_CURRENT_READER_STATE newState)
            {
                Console.WriteLine("\t\t =============== State changed to: " + newState.ToString() + " ===============");
            }

            // event handler for the state changed event
            public void onGpioValuesChanged(ulong gpioValues)
            {
                Console.WriteLine("\t\t GPIO Values Changed: = 0x{0:X08}", gpioValues);
            }


            protected ulong _tagReadCount = 0;

            // event handler for cyclic inventory event
            public void onCyclicInventory(CSrfeProtocolHandler.TagEvent tagEvent)
            {
                Console.Write("\t\t{0:D8}  {1} ", ++_tagReadCount, BitConverter.ToString(tagEvent.tagId));

                if (tagEvent.hasAntenna)
                {
                    Console.Write("@ANT#{0}", tagEvent.antennaId);
                }

                if (tagEvent.hasMemory)
                {
                    Console.Write("MEM {0} @{1}.{2} ", BitConverter.ToString(tagEvent.memData), tagEvent.memBank, tagEvent.memAddr);
                }

                if (tagEvent.hasApplicationInfo)
                {
                    Console.Write("APP ");

                    ///////////////////////////////////////////////////////////////////////
                    // SMARFIT
                    // expect at least 2byte 
                    if (tagEvent.applicationInfo.Count() >= 2)
                    {
                        ushort temp_data = 0;
                        temp_data = (ushort)(tagEvent.applicationInfo[0] & 0x03);
                        temp_data <<= 8;
                        temp_data += (ushort)tagEvent.applicationInfo[1];

                        // standard formula for standard config
                        double temp = (double)temp_data;
                        temp *= 0.18;
                        temp -= 89.3;

                        Console.Write("Temp: {0} °C ({1})", temp, temp_data);
                    }
                    else
                    {
                        Console.Write("{0} ", BitConverter.ToString(tagEvent.applicationInfo));
                    }
                }

                Console.WriteLine("");
            }


            protected Semaphore _storedTagIdSema = null;
            protected byte[] _storedTagId = null;
            protected List<CSrfeProtocolHandler.TagEvent> _storedTagEvent = new List<CSrfeProtocolHandler.TagEvent>();
            
            // event handler for cyclic inventory event
            public void onCyclicInventoryStoreTagId(CSrfeProtocolHandler.TagEvent tagEvent)
            {
                if (_storedTagId == null)
                {
                    _storedTagId = new byte[tagEvent.tagId.Length];
                    tagEvent.tagId.CopyTo(_storedTagId, 0);
                }

                _storedTagEvent.Add(new CSrfeProtocolHandler.TagEvent(tagEvent));

                if(_storedTagIdSema != null )
                    _storedTagIdSema.Release();
            }

            public void onErrorOccured(byte[] data)
            {
                if (data.Length < 1)
                    return;

                byte errCode = data[0];
                byte[] payload = new byte[data.Length - 1];
                Array.Copy(data, 1, payload, 0, payload.Length);
                Constants.eRFE_ERR_CODE code = (Constants.eRFE_ERR_CODE)errCode;
                Console.Write("ERROR: " + code.ToString() + " -> " + BitConverter.ToString(payload) + "\n\r");
            }

        #endregion Event Handler


        #region Helper
            protected bool check_antennaSelection()
            {
                byte antennaCount = 0;
                if (!_ph.getAntennaCount(out antennaCount))
                {
                    Console.WriteLine("ERROR: Could not get the antenna count.");
                    return false;
                }
                if (antennaCount <= 1)
                    return true;

                List<Tuple<byte, ulong>> sequence;
                if (!_ph.getAntennaSequence(out sequence))
                {
                    Console.WriteLine("ERROR: Could not get the antenna sequence.");
                    return false;
                }

                if (sequence.Count() == 1)
                    return true;

                // Get input
                Console.Write("Which antenna should be used? [1-{0}] (1)> ", antennaCount);
                string aS = Console.ReadLine();

                // Check input and parse value
                byte antennaSetup = 1;
                if (aS == "")
                    antennaSetup = 1;
                else
                {
                    try
                    {
                        antennaSetup = Convert.ToByte(aS);
                    }
                    catch (System.Exception)
                    {
                        Console.WriteLine("\t Could not convert the given string \"{0}\" to an integer.", aS);
                        return false;
                    }
                }

                if (!_ph.setWorkingAntenna(antennaSetup))
                {
                    Console.WriteLine("ERROR: Could not set working antenna.");
                    return false;
                }

                return true;
            }

            private CSrfeProtocolHandler createInstance(IProtocolDeviceInterface device, ReaderType type)
            {
                CSrfeProtocolHandler p = createInstanceExtended(device, type);

                if (p != null)
                    return p;

                switch (type)
                {
                    case ReaderType.STANDARD:
                        return new CSrfeProtocolHandler(device);
                }

                return null;
            }

            private void printTestTable()
            {
                Console.WriteLine("+------------------------ MENU ----------------------------+");
                Console.WriteLine("|    q = Quit                                              |");
                Console.WriteLine("|    1 = Query Reader Information                          |");
                Console.WriteLine("|    2 = Test Attenuation Settings                         |");
                Console.WriteLine("|    3 = Read Frequency Settings                           |");
                Console.WriteLine("|    4 = Read Sensitivity Settings                         |");
                Console.WriteLine("|    5 = Test Heartbeat                                    |");
                Console.WriteLine("|    6 = Test GPIO                                         |");
                Console.WriteLine("|    8 = Get Antenna Count                                 |");
                Console.WriteLine("|   10 = Do SingleInventory                                |");
                Console.WriteLine("|   11 = Start Inventory                                   |");
                Console.WriteLine("|   31 = Try to Read TID & UM                              |");
                Console.WriteLine("|   32 = Try to Read/Write User Mem                        |");
                Console.WriteLine("|   41 = Test Read TID of First Tag - Slow                 |");
                Console.WriteLine("+----------------------------------------------------------+");
                printTestTableExtended();
            }

            private bool callTestRoutine(int id)
            {
                if (id == 1)
                    test_ReaderInfo();
                else if (id == 2)
                    test_Attenuation();
                else if (id == 3)
                    test_Frequency();
                else if (id == 4)
                    test_Sensitivity();
                else if (id == 5)
                    test_Heartbeat();
                else if (id == 6)
                    test_GPIO();
                else if (id == 8)
                    test_AntennaSetting();
                else if (id == 10)
                    test_SingleInventory();
                else if (id == 11)
                    test_Inventory();

                else if (id == 31)
                    test_ReadTIDUM();
                else if (id == 32)
                    test_ReadWriteUser();

                else if (id == 41)
                    ReadTIDFirstTag_Slow();

                else
                    return callTestRoutinesExtended(id);

                return true;
            }

        #endregion


        #region Overridables

            public virtual bool getTestConnection(out IProtocolDeviceInterface dev, out ReaderType type)
            {
                dev = null;
                type = ReaderType.STANDARD;
                return false;
            }

            protected virtual CSrfeProtocolHandler createInstanceExtended(IProtocolDeviceInterface device, ReaderType type)
            {
                return null;
            }

            protected virtual void printTestTableExtended()
            {
            }

            protected virtual bool callTestRoutinesExtended(int id)
            {
                return false;
            }
        
        #endregion

    }
}
